/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.aksw.treelearning.learner;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.aksw.treelearning.data.Cache;
import org.aksw.treelearning.data.CsvReader;
import org.aksw.treelearning.data.Mapping;
import org.aksw.treelearning.data.SetOperations;
import org.aksw.treelearning.evaluation.PRFComputer;
import org.aksw.treelearning.pseudomeasures.Measure;
import org.aksw.treelearning.pseudomeasures.PM1;
import org.aksw.treelearning.tree.Condition;
import org.aksw.treelearning.utils.Utils;

/**
 *
 * @author ngonga
 */
public class BasicLearner extends ElementaryLearner {

    public static double LOWERBOUND = 0.3;
    public static int Q = 3;
    public static Measure measure;

    public BasicLearner() {
        measure = new PM1();
    }

    /**
     *
     * @param source Source cache
     * @param target Target cache
     * @param sourceProperty Property from source cache
     * @param targetProperty Property from target cache
     * @param delta Number of search iteration
     * @return Best threshold for this particular configuration of a node
     */
    public Condition getBestCondition(Cache source, Cache target, String sourceProperty, String targetProperty, int gridSize) {

        double delta = (1d - LOWERBOUND) / ((double) gridSize);
        Mapping m = Utils.computeMapping(source, target, sourceProperty, targetProperty, LOWERBOUND, Q);
        double fMax = measure.getPseudoFMeasure(source.getAllUris(), target.getAllUris(), m, 1.0);
        double currentF;
        Mapping currentMapping;
        double bestThreshold = LOWERBOUND;
        System.out.println(sourceProperty + "\n=============");
        for (int i = 0; i < gridSize; i++) {
            double threshold = LOWERBOUND + ((double) (i + 1)) * delta;
            currentMapping = m.getSubMap(threshold);
            currentF = measure.getPseudoFMeasure(source.getAllUris(), target.getAllUris(), currentMapping, 1.0);
            System.out.println(threshold + "\t" + currentF);
            if (currentF > fMax) {
                fMax = currentF;
                bestThreshold = threshold;
            }
        }
        Condition condition = new Condition(sourceProperty, targetProperty, bestThreshold, Q);
        System.out.println("Best = " + fMax + "\t" + bestThreshold);
        condition.fMeasure = fMax;
        condition.mapping = m.getSubMap(bestThreshold);
        return condition;
    }

    public Mapping learnTree(String sourceFile, String targetFile, Map<String, String> propertyMapping, int gridSize) {


        Cache source = CsvReader.readFile(sourceFile);
        Cache target = CsvReader.readFile(targetFile);
        System.out.println("Source = " + source.size() + ", Target = " + target.size());
        List<Condition> conditions = new ArrayList<Condition>();
        String tree = "";
        //first generate conditions

        for (String sourceProperty : propertyMapping.keySet()) {
            long begin = System.currentTimeMillis();
            Condition cond = getBestCondition(source, target, sourceProperty, propertyMapping.get(sourceProperty), gridSize);
            long end = System.currentTimeMillis();
            System.out.println("required " + (end - begin) + " ms for comuputation of property <" + sourceProperty + ">");
            conditions.add(cond);
        }

        //now combine them
        //initial current mapping is empty, initial f is 0

        Mapping currentMapping = new Mapping();
        double currentF = 0d;
        int count = conditions.size();
        while (count > 0) {
            count--;
            //get best condition
            Condition bestCondition = conditions.get(0);
            int index = 0;
            for (int i = 1; i < conditions.size(); i++) {
                if (conditions.get(i).fMeasure > bestCondition.fMeasure) {
                    bestCondition = conditions.get(i);
                    index = i;
                }
            }
            conditions.remove(index);

            String cond = "sim(" + bestCondition.sourceProperty + " >= " + bestCondition.threshold + ")";
            //now look for best combination with current mapping
            List<Mapping> mappingList = new ArrayList<Mapping>();
            mappingList.add(SetOperations.union(bestCondition.mapping, currentMapping));
            mappingList.add(SetOperations.intersection(bestCondition.mapping, currentMapping));
            mappingList.add(SetOperations.difference(bestCondition.mapping, currentMapping));
            mappingList.add(SetOperations.difference(currentMapping, bestCondition.mapping));

            Mapping bestMapping = currentMapping;
            double fMax = currentF;
            int bestIndex = -1;
            for (int i = 0; i < mappingList.size(); i++) {
                double f = measure.getPseudoFMeasure(source.getAllUris(), target.getAllUris(), mappingList.get(i), 1.0);
                System.out.println(i + " -> " + f);
                //get best f
                if (f > fMax) {
                    bestMapping = mappingList.get(i);
                    fMax = f;
                    bestIndex = i;
                    System.out.println("Best index = " + bestIndex);
                }
            }
            if (bestIndex >= 0) {
                if (bestIndex == 0) {
                    tree = "(" + tree + " UNION " + cond + ")";
                } else if (bestIndex == 1) {
                    tree = "(" + tree + " INTERSECTION " + cond + ")";
                } else if (bestIndex == 2) {
                    tree = "(" + cond + " MINUS " + tree + ")";
                } else {
                    tree = "(" + tree + " MINUS " + cond + ")";
                }
            }
            currentMapping = bestMapping;
            currentF = fMax;

            System.out.println("Current Pseudo-F: " + currentF);
        }
        System.out.println(tree);
        Mapping one2nMapping = currentMapping.getBestOneToOneMapping();
        Mapping one2oneMapping = currentMapping.forceOneToOne(currentMapping);

        double f12n = measure.getPseudoFMeasure(source.getAllUris(), target.getAllUris(), one2nMapping, 1.0);
        double f11n = measure.getPseudoFMeasure(source.getAllUris(), target.getAllUris(), one2oneMapping, 1.0);

        System.out.println("F-measure\t" + currentF);
        System.out.println("1-n F-measure\t" + f12n);
        System.out.println("1-1 F-measure\t" + f11n);
        return currentMapping;
//        double f = currentF;
//        if(f12n > f) f = f12n;
//        if(f11n > f) f = f11n;
//        
//        if(f==currentF) return currentMapping;
//        if(f==f12n) return one2nMapping;
//        return one2oneMapping;
    }

    public static void main(String args[]) {
        long begin = System.currentTimeMillis();
        Map<String, String> propertyMapping = new HashMap<>();

//        String sourceFile = "resources/Amazon-GoogleProducts/Amazon.csv";
//        String targetFile = "resources/Amazon-GoogleProducts/GoogleProducts.csv";
//        Mapping reference = Mapping.readFromCsvFile("resources/Amazon-GoogleProducts/Amzon_GoogleProducts_perfectMapping.csv");
//        propertyMapping.put("name", "title");
//        propertyMapping.put("description", "description");
//        propertyMapping.put("manufacturer", "manufacturer");

//        String sourceFile = "resources/Abt-Buy/Abt.csv";
//        String targetFile = "resources/Abt-Buy/Buy.csv";
//        Mapping reference = Mapping.readFromCsvFile("resources/Abt-Buy/abt_buy_perfectMapping.csv");
//        propertyMapping.put("name", "name");
//        propertyMapping.put("description", "description");


        String sourceFile = "resources/DBLP-ACM/DBLP.csv";
        String targetFile = "resources/DBLP-ACM/ACM.csv";
        Mapping reference = Mapping.readFromCsvFile("resources/DBLP-ACM/DBLP-ACM.csv");
        propertyMapping.put("title", "title");
        propertyMapping.put("authors", "authors");
        propertyMapping.put("venue", "venue");


//        String sourceFile = "resources/DBLP-Scholar/DBLP.csv";
//        String targetFile = "resources/DBLP-Scholar/Scholar.csv";
//        Mapping reference = Mapping.readFromCsvFile("resources/DBLP-Scholar/DBLP-Scholar-matches.csv");
//        propertyMapping.put("title", "title");
//        propertyMapping.put("authors", "authors");
//        propertyMapping.put("venue", "venue");

        Mapping m = new BasicLearner().learnTree(sourceFile, targetFile, propertyMapping, 10);
        long end = System.currentTimeMillis();

        System.out.println("Whole computation took " + (end - begin) + " ms.");
        System.out.println("F-Measure = " + new PRFComputer().computeFScore(m, reference));
        System.out.println("F-Measure (1-n) = " + new PRFComputer().computeFScore(m.getBestOneToOneMapping(), reference));
        System.out.println("F-Measure (1-1)= " + new PRFComputer().computeFScore(Mapping.forceOneToOne(m), reference));
    }
}
